package com.whfc.common.util;

import net.coobird.thumbnailator.Thumbnails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;

/**
 * 图片处理工具类型
 */
public class ImageUtil {

    private final static Logger logger = LoggerFactory.getLogger(ImageUtil.class);

    /**
     * 得到文件流
     *
     * @param url 网络图片URL地址
     * @return 图片 byte[]
     */
    public static byte[] getFileBytes(String url) {
        logger.info("根据文件地址获取文件字节, url:{}", url);
        try {
            URL httpUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5 * 1000);
            //通过输入流获取图片数据
            InputStream inStream = conn.getInputStream();
            //得到图片的二进制数据
            return readInputStream(inStream);
        } catch (Exception e) {
            logger.warn("根据文件地址获取文件字节失败, error:{}", e.getMessage());
        }
        return null;
    }

    /**
     * Base64解码
     *
     * @param baseDecoder base64编码
     * @return 字节数组
     */
    public static byte[] base64ToImage(String baseDecoder) {
        logger.info("将base64字符串转换为字节数组");
        byte[] decodeByte = null;
        try {
            decodeByte = Base64.getDecoder().decode(baseDecoder);
            for (int i = 0; i < decodeByte.length; ++i) {
                //调整异常数据
                if (decodeByte[i] < 0) {
                    decodeByte[i] += 256;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return decodeByte;
    }

    /**
     * 文件转换成base64
     *
     * @param file 文件
     * @return base64编码
     */
    public static String base64Encode(File file) {
        logger.info("将将文件转为base64字符串");
        FileInputStream fis = null;
        byte[] data = null;
        try {
            fis = new FileInputStream(file);
            data = new byte[fis.available()];
            fis.read(data);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        //对字节数组Base64编码
        return Base64.getEncoder().encodeToString(data);
    }

    /**
     * 将字节数组转为Base64字符串
     *
     * @param imgByte 文件字节
     * @return Base64字符串
     */
    public static String base64Encode(byte[] imgByte) {
        logger.info("将文件转为Base64字符串");
        // 对字节数组Base64编码
        return Base64.getEncoder().encodeToString(imgByte);
    }

    /**
     * 获取文件输入流
     *
     * @param url 文件地址
     * @return 文件输入流
     */
    public static InputStream getFileStream(String url) {
        logger.info("根据文件地址获取输入流, url:{}", url);
        try {
            URL httpUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5 * 1000);
            return conn.getInputStream();
        } catch (Exception e) {
            logger.warn("根据文件地址获取输入流失败, error:{}", e.getMessage());
        }
        return null;
    }


    /**
     * 从输入流中获取byte[]
     *
     * @param inStream 输入流
     * @return 图片 byte[]
     */
    public static byte[] readInputStream(InputStream inStream) {
        logger.info("将输入流转为字节数组");
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        try {
            while ((len = inStream.read(buffer)) != -1) {
                outStream.write(buffer, 0, len);
            }
        } catch (Exception e) {
            logger.warn("流读取失败.", e);
        } finally {
            try {
                inStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return outStream.toByteArray();
    }

    /**
     * 根据指定大小压缩图片
     *
     * @param is          输入流
     * @param desFileSize 指定图片大小，单位kb
     * @return 压缩质量后的图片输入流
     */
    public static InputStream compressPicForScale(InputStream is, long desFileSize) {
        byte[] bytes = readInputStream(is);
        byte[] fileBytes = compressPicForScale(bytes, desFileSize);
        return new ByteArrayInputStream(fileBytes);
    }


    /**
     * 根据指定大小压缩图片
     *
     * @param imageBytes  源图片字节数组
     * @param desFileSize 指定图片大小，单位kb
     * @return 压缩质量后的图片字节数组
     */
    public static byte[] compressPicForScale(byte[] imageBytes, long desFileSize) {
        logger.info("将图片压缩至指定大小, desFileSize:{}", desFileSize);
        if (imageBytes == null || imageBytes.length <= 0 || imageBytes.length < desFileSize * 1024) {
            return imageBytes;
        }
        long srcSize = imageBytes.length;
        double accuracy = getAccuracy(srcSize / 1024);
        try {
            while (imageBytes.length > desFileSize * 1024) {
                ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length);
                Thumbnails.of(inputStream)
                        .scale(accuracy)
                        .outputQuality(accuracy)
                        .toOutputStream(outputStream);
                imageBytes = outputStream.toByteArray();
                outputStream.close();
                inputStream.close();
            }
            logger.info("图片原大小={}kb | 压缩后大小={}kb", srcSize / 1024, imageBytes.length / 1024);
        } catch (Exception e) {
            logger.error("图片压缩失败.", e);
        }
        return imageBytes;
    }

    /**
     * 自动调节精度(经验数值)
     *
     * @param size 源图片大小
     * @return 图片压缩质量比
     */
    private static double getAccuracy(long size) {
        double accuracy;
        if (size < 900) {
            accuracy = 0.85;
        } else if (size < 2047) {
            accuracy = 0.6;
        } else if (size < 3275) {
            accuracy = 0.44;
        } else {
            accuracy = 0.4;
        }
        return accuracy;
    }

    /**
     * 网络图片压缩成base64图片
     * @param url
     * @param size
     * @return
     */
    public static String base64EncodeAndCompress(String url,int size){
        if (!StringUtils.hasText(url)) {
            return null;
        }
        byte[] fileBytes = getFileBytes(url);
        byte[] bytes = compressPicForScale(fileBytes, size);
        return "data:image/jpeg;base64,"+base64Encode(bytes);
    }

    /**
     * base64图片压缩
     * @param base64
     * @param size
     * @return
     */
    public static String base64Compress(String base64,int size){
        if (!StringUtils.hasText(base64)) {
            return null;
        }
        byte[] bytes = base64ToImage(base64);
        byte[] compressByte = compressPicForScale(bytes, size);
        return "data:image/jpeg;base64,"+base64Encode(compressByte);
    }

    public static void main(String[] args) {
        System.out.println(ImageUtil.base64EncodeAndCompress("https://file.gbuvip.com/file/operationPic/202302182021214517.jpg",40));
    }
}
